home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / BARNET / ARMTEX / SOURCES1 / !TeX / texmf / source / armTeX / lib / c / pathsrch < prev    next >
Encoding:
C/C++ Source or Header  |  1998-05-25  |  16.4 KB  |  584 lines

  1. /*
  2.  * pathsrch.c: look for files based on paths, i.e., colon-separated
  3.  * lists of directories.
  4.  * 
  5.  * Perhaps we should allow % specifiers in the paths for the resolution,
  6.  * etc.
  7.  * 
  8.  * This is a RISC OS ONLY version! Things not needed in RISC OS are cut
  9.  * out
  10.  * 
  11.  * Copyright (C) 1992 Free Software Foundation, Inc.
  12.  * 
  13.  * This program is free software; you can redistribute it and/or modify
  14.  * it under the terms of the GNU General Public License as published
  15.  * by the Free Software Foundation; either version 2, or (at your
  16.  * option) any later version.
  17.  * 
  18.  * This program is distributed in the hope that it will be useful, but
  19.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  21.  * General Public License for more details.
  22.  * 
  23.  * You should have received a copy of the GNU General Public License
  24.  * along with this program; if not, write to the Free Software
  25.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  26.  */
  27.  
  28. #include "config.h"
  29.  
  30. #include "c-pathch.h"
  31. #include "c-namemx.h"
  32. #include "c-pathmx.h"
  33. #include "paths.h"
  34. #include "c-ctype.h"
  35. #include "riscos_ex.h"
  36. #include "pathsrch.h"
  37.  
  38. #include "OS:OSFile.h"
  39. #include "OS:OSGBPB.h"
  40.  
  41. static void add_directory P3H (string **, unsigned *, string);
  42. static int expand_subdir P3H (string **, unsigned *, string);
  43. static void expand_pseudo_dir P4H (string **, unsigned *, string, int);
  44. static string readable P1H (string);
  45. #if 0
  46. static string *find_dir_list P1H (string);
  47. static void save_dir_list P2H (string, string *);
  48. #endif
  49. boolean riscos_readaccess P1H (string);
  50.  
  51. /* This special value is inserted in the dirlist to indicate
  52.    points which the dirnames cannot cross when shuffled */
  53. static const string special_dir_marker = "$$marker$$";
  54.  
  55. /*
  56.  * If FILENAME is absolute or explicitly relative (i.e., starts with
  57.  * `@.', or `^.' or there's a ':', '&', '\' or '$' in it), or if
  58.  * DIR_LIST is null, we return whether FILENAME is readable as-is.
  59.  * Otherwise, we test if FILENAME is in any of the directories listed
  60.  * in DIR_LIST.  (The last entry of DIR_LIST must be null.)  We
  61.  * return the complete path if found, NULL else.
  62.  * 
  63.  * In the interests of doing minimal work here, we assume that each
  64.  * element of DIR_LIST already ends with a `.'.
  65.  * 
  66.  * DIR_LIST is most conveniently made by calling `initialize_path_list'.
  67.  * This is a separate routine because we allow recursive searching,
  68.  * and it may take some time to discover the list of directories.  We
  69.  * do not want to incur that overhead every time we want to look for
  70.  * a file.
  71.  * 
  72.  * (Actually, `.' is not hardwired into this routine; we use PATH_SEP,
  73.  * defined above.)
  74.  */
  75.  
  76. string
  77. find_path_filename P2C (string, filename, string *, dir_list)
  78. {
  79.   string found_name = NULL;
  80.   string *dir_list_top = dir_list;
  81.  
  82.   /* 
  83.    * If FILENAME is absolute or explicitly relative, or if DIR_LIST
  84.    * is null, only check if FILENAME is readable.
  85.    */
  86.   if (riscos_absolute (filename) || dir_list == NULL)
  87.     found_name = readable (filename);
  88.   else
  89.     {                /* Test if FILENAME is in any of the *
  90.                    directories in DIR_LIST.  */
  91.       string save_filename = filename;
  92.  
  93.       while (*dir_list != NULL)
  94.     {
  95.       filename = concat (*dir_list, save_filename);
  96.  
  97.       found_name = readable (filename);
  98.       if (found_name == NULL)
  99.         {
  100.           free (filename);
  101.           if (*++dir_list == special_dir_marker)
  102.         do
  103.           dir_list_top = ++dir_list;
  104.         while (*dir_list == special_dir_marker);
  105.         }
  106.       else
  107.         {
  108.           string save_dir;
  109.           /* Move the directory to the top of the list */
  110.           save_dir = *dir_list;
  111.           memmove (dir_list_top + 1, dir_list_top,
  112.                (dir_list - dir_list_top) * sizeof (string));
  113.           *dir_list_top = save_dir;
  114.  
  115.           if (found_name != filename)
  116.         free (filename);
  117.           break;
  118.         }
  119.     }
  120.     }
  121.  
  122.   return found_name;
  123. }
  124.  
  125.  
  126. /*
  127.  * If NAME is readable, return it.  If the error is ENAMETOOLONG,
  128.  * truncate any too-long path components and return the result
  129.  * (unless there were no too-long components, i.e., a overall
  130.  * too-long name caused the error, in which case return NULL).  On
  131.  * any other error, return NULL.
  132.  * 
  133.  * POSIX invented this brain-damage of not necessarily truncating
  134.  * pathname components; the system's behavior is defined by the value
  135.  * of the symbol _POSIX_NO_TRUNC, but you can't change it
  136.  * dynamically!
  137.  * 
  138.  * RISC OS does things a bit differently. We need some kernel routines
  139.  * for testing read access, and we don't truncate filenames, as
  140.  * different filing systems might allow different filename lengths.
  141.  */
  142.  
  143. static string
  144. readable (string name)
  145. {
  146.   string newname;
  147.  
  148. #ifdef RISCOS_DEBUG
  149.   fprintf (stderr, "readable(\"%s\")\n", name);
  150. #endif
  151.   newname = riscos_translate (name, 0);
  152. #ifdef RISCOS_DEBUG
  153.   if (newname)
  154.     fprintf (stderr, " yes --> \"%s\"\n", newname);
  155. #endif
  156.   return newname;
  157. }
  158.  
  159. /* OS_File 17: prm p.836 or c.riscos_ex */
  160.  
  161. /* Return true if name is a file with owner or world read access */
  162. boolean
  163. riscos_readaccess (string name)
  164. {
  165.   int obj_type;
  166.   fileswitch_attr attr;
  167.  
  168.   if (NULL == xosfile_read_stamped (name,
  169.                   &obj_type, NULL, NULL, NULL, &attr, NULL))
  170.     return (obj_type == 1) && (attr & 0x11);
  171.   else
  172.     return false;
  173. }
  174.  
  175.  
  176. /*
  177.  * Return a NULL-terminated array of directory names, each name
  178.  * ending with PATH_SEP, created by parsing the
  179.  * PATH_DELIMITER-separated list in the value of the environment
  180.  * variable ENV_NAME, or DEFAULT_PATH if the envvar is not set.
  181.  * 
  182.  * A leading or trailing colon in the value of ENV_NAME is replaced by
  183.  * DEFAULT_PATH.
  184.  * 
  185.  * Any element of the path that ends with double PATH_SEP characters
  186.  * (e.g., `foo..') is replaced by all its subdirectories.
  187.  * 
  188.  * If ENV_NAME is null, only parse DEFAULT_PATH.  If both are null, do
  189.  * nothing and return NULL.
  190.  * 
  191.  * Under RISC OS all paths have "$Path" appended.
  192.  */
  193.  
  194. string *
  195. initialize_path_list (string env_name, string default_path)
  196. {
  197.   string dir, path;
  198.   string *dir_list = NULL;
  199.   unsigned dir_count = 0;
  200.   string env_value = NULL;
  201.   string orig_path;
  202.  
  203.   if (env_name)
  204.     {
  205.       string path_name;
  206.       path_name = concat (env_name, "$Path");
  207.       env_value = getenv (path_name);
  208.       free (path_name);
  209.     }
  210.   orig_path = expand_default (env_value, default_path);
  211.  
  212.   if (orig_path == NULL || *orig_path == 0)
  213.     return NULL;
  214.  
  215.   /* 
  216.    * If we've already seen this colon-separated list, then just get
  217.    * it back instead of going back to the filesystem.
  218.    */
  219. #if 0
  220.   dir_list = find_dir_list (orig_path);
  221.   if (dir_list != NULL)
  222.     return dir_list;
  223. #endif
  224.  
  225.   if (*orig_path == PATH_DELIMITER)
  226.     add_directory (&dir_list, &dir_count, "@.");
  227.  
  228.   path = concat (PATH_DELIMITER_STRING, orig_path);
  229. #ifdef RISCOS_DEBUG
  230.   fprintf (stderr, "Parsing \"%s\"\n", path);
  231. #endif
  232.  
  233.   /* Find each element in the path in turn.  */
  234.   for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
  235.        dir = strtok (NULL, PATH_DELIMITER_STRING))
  236.     {
  237.       int len;
  238.  
  239.       len = strlen (dir);
  240.  
  241.       /* If `dir' is the empty string, skip it.  */
  242.       if (len == 0)
  243.     continue;
  244.  
  245.       /* One of these for each comma */
  246.       if (dir_count > 0)
  247.     add_directory (&dir_list, &dir_count, special_dir_marker);
  248.  
  249.       /* 
  250.        * If `dir' ends in double dots, do subdirectories (and remove
  251.        * the second dot, so the final pathnames we return don't look
  252.        * like foo..bar.).  Because we obviously want to do
  253.        * subdirectories of `dir', we don't check if it is a leaf.  This
  254.        * means that if `dir' is `foo..', and `foo' contains only
  255.        * symlinks (so our leaf test below would be true), the symlinks
  256.        * are chased.
  257.        */
  258.       if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP)
  259.     {
  260.       dir[len - 1] = 0;
  261.       if (riscos_isdir (dir))
  262.         {
  263.           /* expand pseudo filing systems also */
  264.           expand_pseudo_dir (&dir_list, &dir_count, dir, 0);
  265.         }
  266.     }
  267.       else
  268.     {            /* Don't bother to add the directory * if it
  269.                    doesn't exist.  */
  270.       if (riscos_isdir (dir))
  271.         add_directory (&dir_list, &dir_count, dir);
  272.     }
  273.     }
  274.  
  275.   /* Add the terminating null entry to `dir_list'.  */
  276.   dir_count++;
  277.   XRETALLOC (dir_list, dir_count, string);
  278.   dir_list[dir_count - 1] = NULL;
  279.  
  280. #if 0
  281.   /* Save the directory list we just found.  */
  282.   save_dir_list (orig_path, dir_list);
  283. #endif
  284.  
  285. #ifdef RISCOS_DEBUG
  286.   {
  287.     int i = 0;
  288.     fputs ("These are the cached directories:\n", stderr);
  289.     while (dir_list[i])
  290.       fprintf (stderr, "  \"%s\"\n", dir_list[i++]);
  291.   }
  292. #endif
  293.  
  294.   return dir_list;
  295. }
  296.  
  297.  
  298. /* Subroutines for `initialize_path_list'.  */
  299.  
  300. /*
  301.  * Add a newly-allocated copy of DIR to the end of the array pointed
  302.  * to by DIR_LIST_PTR. Increment DIR_COUNT_PTR. UNIX:  Append a `/'
  303.  * to DIR if necessary. We assume DIR is a directory, to avoid
  304.  * unnecessary an unnecessary call to `stat'.
  305.  */
  306.  
  307. static void
  308. add_directory (dir_list_ptr, dir_count_ptr, dir)
  309.      string **dir_list_ptr;
  310.      unsigned *dir_count_ptr;
  311.      string dir;
  312. {
  313.   /* Add `dir' to the list of the directories.  */
  314.   (*dir_count_ptr)++;
  315.   XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
  316.   (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
  317. #ifdef RISCOS_DEBUG
  318.   fprintf (stderr, "  adding subdir \"%s\"\n", dir);
  319. #endif
  320. }
  321.  
  322.  
  323. /*
  324.  * Add DIRNAME to DIR_LIST and look for subdirectories, recursively.
  325.  * We assume DIRNAME is the name of a directory.
  326.  */
  327.  
  328. /* RISC OS version (does full recursive search) */
  329.  
  330. /* The size of the buffer passes to OS_GBPB */
  331. #define osgbpb_bufsize 64
  332.  
  333. static int
  334. expand_subdir (dir_list_ptr, dir_count_ptr, dirname)
  335.      string **dir_list_ptr;
  336.      unsigned *dir_count_ptr;
  337.      string dirname;
  338. {
  339.   osgbpb_info gbpb_buffer[osgbpb_bufsize];
  340.   int context, count;
  341.   int good_dir = 0;        /* Set to 1 if dir has subdirs or * files
  342.                    with '/' */
  343.   char potential[PATH_MAX];
  344.   unsigned length;
  345.  
  346.   /* Compute the length of DIRNAME, since it's loop-invariant.  */
  347.   length = strlen (dirname);
  348.  
  349.   /* Construct the part of the pathname that doesn't change. */
  350.   strcpy (potential, dirname);
  351.   if (potential[length - 1] == PATH_SEP)
  352.     potential[--length] = 0;
  353.  
  354. #ifdef RISCOS_DEBUG
  355.   fprintf (stderr, "expanding subdir \"%s\"\n", potential);
  356. #endif
  357.  
  358.   /* 
  359.    * If this directory contains a file named !NoExpand then we don't
  360.    * scan subdirs
  361.    */
  362.   strcpy (potential + length, ".!NoExpand");
  363.   if (riscos_readaccess (potential))
  364.     {
  365. #ifdef RISCOS_DEBUG
  366.       fprintf (stderr, "stop, met \"%s\"\n", potential);
  367. #endif
  368.       return 1;            /* pretend it's a good dir */
  369.     }
  370.   potential[length] = '\0';    /* remove leafname */
  371.  
  372.   /* prepare OS_GBPB calls */
  373.   context = 0;
  374.   do
  375.     {
  376.       osgbpb_info *object;
  377.       char *newfound;
  378.  
  379.       if (NULL != xosgbpb_dir_entries_info (potential,
  380.                (osgbpb_info_list *) gbpb_buffer, osgbpb_bufsize,
  381.                     context, sizeof (gbpb_buffer), NULL,
  382.                         &count, &context))
  383.     break;            /* some error in OS_GBPB */
  384.  
  385.       object = gbpb_buffer;
  386.       while (count > 0)
  387.     {
  388.       switch (object->obj_type)
  389.         {
  390.         case 1:        /* This is a file */
  391.           if (!good_dir && strchr (object->name, '/') != NULL)
  392.         good_dir = 1;    /* file with '/' ext */
  393.           break;
  394.         case 2:        /* This is a directory */
  395.         case 3:        /* This is an image file */
  396.           good_dir = 1;    /* sub-dir found */
  397.           newfound = xmalloc (length + strlen (object->name) + 3);
  398.           strcpy (newfound, potential);
  399.           strcpy (newfound + length, ".");
  400.           strcat (newfound + length, object->name);
  401.           strcat (newfound + length, ".");
  402.           /* Now we recursively expand and add if its good */
  403.           if (expand_subdir (dir_list_ptr, dir_count_ptr, newfound))
  404.         add_directory (dir_list_ptr, dir_count_ptr, newfound);
  405.           else
  406.         free (newfound);
  407.           break;
  408.         }
  409.       /* Find the next element in the buffer. */
  410.       count--;
  411.       object = (osgbpb_info *) ((char *) (object + 1)
  412.                     + (strlen (object->name) & ~3));
  413.     }
  414.     }
  415.   while (context != -1);
  416. #ifdef RISCOS_DEBUG
  417.   fprintf (stderr, "end of subdir(%d) \"%s\"\n", good_dir, potential);
  418. #endif
  419.   return good_dir;
  420. }
  421.  
  422. /* Expand pseudo-filing systems that uses path variables. We need to do this to ensure that we get all subdirectories */
  423.  
  424. /* Add all the subdirectories of dirname to dir_list after expanding pseudo-filing systems. */
  425.  
  426. static void
  427. expand_pseudo_dir (string ** dir_list_ptr, unsigned *dir_count_ptr,
  428.            string dirname, int recursion_level)
  429. {
  430.   string col_pos;
  431.   string path_value;
  432.   string prefix;
  433.   size_t prefix_size, postfix_size;
  434.   string new_dir_name;
  435.  
  436. #ifdef RISCOS_DEBUG
  437.   fprintf (stderr, "expand_pseudo(%d)(\"%s\")\n", recursion_level, dirname);
  438. #endif
  439.   col_pos = strchr (dirname, ':');
  440.   if (col_pos != NULL)
  441.     {
  442.       string path_var_name;
  443.  
  444.       path_var_name = xmalloc (col_pos - dirname + 6);
  445.       strncpy (path_var_name, dirname, col_pos - dirname);
  446.       strcpy (path_var_name + (col_pos - dirname), "$Path");
  447.       path_value = getenv (path_var_name);
  448. #ifdef RISCOS_DEBUG
  449.       fprintf (stderr, "Looking for <%s>\n", path_var_name);
  450. #endif
  451.       free (path_var_name);
  452.     }
  453.   /* The test for recursion_level is to avoid infinite recursion when
  454.      somebody stupidly says "Set Texinputs$Path texinputs:" */
  455.   if (col_pos == NULL || path_value == NULL || recursion_level > 10)
  456.     {
  457.       if (expand_subdir (dir_list_ptr, dir_count_ptr, dirname))
  458.     add_directory (dir_list_ptr, dir_count_ptr, dirname);
  459.       else if (recursion_level > 0)
  460.     free (dirname);        /* only free from recursive calls */
  461.       return;            /* not more to do */
  462.     }
  463.   /* we cannot trust the string returned by getenv() to remain valid */
  464.   path_value = prefix = xstrdup (path_value);
  465.   postfix_size = strlen (col_pos + 1);
  466. #ifdef RISCOS_DEBUG
  467.   fprintf (stderr, "Expanding path \"%s\"\n", path_value);
  468.   fprintf (stderr, " postfix = \"%s\"\n", col_pos + 1);
  469. #endif
  470.   /* path_value is a comma/space separated list of prefixes to try. strtok()
  471.      would be ideal here, but it's not reentrant and already used in
  472.      find_path_filename(). We go for strcspn()/strspn() */
  473.   do
  474.     {
  475.       prefix_size = strcspn (prefix, ", ");
  476.       new_dir_name = xmalloc (prefix_size + postfix_size + 1);
  477.       strncpy (new_dir_name, prefix, prefix_size);
  478.       strcpy (new_dir_name + prefix_size, col_pos + 1);
  479.       expand_pseudo_dir (dir_list_ptr, dir_count_ptr,
  480.              new_dir_name, recursion_level + 1);
  481.       if ((*dir_list_ptr)[*dir_count_ptr-1] != special_dir_marker)
  482.     add_directory (dir_list_ptr, dir_count_ptr, special_dir_marker);
  483.  
  484.       /* point to the delimiters */
  485.       prefix += prefix_size;
  486.       /* point beyond the delimiters */
  487.       prefix += prefix_size = strspn (prefix, ", ");
  488.     }
  489.   while (prefix_size > 0);    /* repeat is there was a comma or a space */
  490.   free (path_value);
  491. }
  492.  
  493. /*
  494.  * These routines, while not strictly needed to be exported, are
  495.  * plausibly useful to be called by outsiders.
  496.  */
  497.  
  498. /*
  499.  * UNIX: Replace a leading or trailing `:' in ENV_PATH with
  500.  * DEFAULT_PATH.  If neither is present, return ENV_PATH if that is
  501.  * non-null, else DEFAULT_PATH.
  502.  * 
  503.  * RISC OS: Haven't found a way to _include_ the defaults yet
  504.  */
  505.  
  506. string
  507. expand_default (env_path, default_path)
  508.      string env_path;
  509.      string default_path;
  510. {
  511.   string expansion;
  512.  
  513.   if (env_path == NULL)
  514.     expansion = default_path;
  515.   else
  516.     expansion = env_path;
  517.  
  518.   return expansion;
  519. }
  520.  
  521.  
  522. #if 0                /* maybe I'll implement this one day */
  523. /*
  524.  * Routines to save and retrieve a directory list keyed by the
  525.  * original colon-separated path.  This is useful because 1) it can
  526.  * take a significant amount of time to discover all the
  527.  * subdirectories of a given directory, and 2) many paths all have
  528.  * the same basic default, and thus would recompute the directory
  529.  * list.
  530.  */
  531.  
  532. typedef struct
  533. {
  534.   string path;
  535.   string *dir_list;
  536. }
  537. saved_path_entry;
  538.  
  539. static saved_path_entry *saved_paths = NULL;
  540. static unsigned saved_paths_length = 0;
  541.  
  542.  
  543. /*
  544.  * We implement the data structure as a simple linear list, since
  545.  * it's unlikely to ever be more than a dozen or so elements long. We
  546.  * don't bother to check here if PATH has already been saved; we
  547.  * always add it to our list.
  548.  */
  549.  
  550. static void
  551.   save_dir_list
  552. P2C (string, path, string *, dir_list)
  553. {
  554.   saved_paths_length++;
  555.   XRETALLOC (saved_paths, saved_paths_length, saved_path_entry);
  556.   saved_paths[saved_paths_length - 1].path = path;
  557.   saved_paths[saved_paths_length - 1].dir_list = dir_list;
  558. }
  559.  
  560. /* When we retrieve, just check the list in order.  */
  561.  
  562. static string *
  563.   find_dir_list
  564. P1C (string, path)
  565. {
  566.   unsigned p;
  567.  
  568. #ifdef RISCOS            /* somehow this function doesn't * work. But
  569.                    as FileCore caches most * things, a
  570.                    research is not that * slow */
  571.   return NULL;
  572. #else
  573.  
  574.   for (p = 0; p < saved_paths_length; p++)
  575.     {
  576.       if (strcmp (saved_paths[p].path, path) == 0)
  577.     return saved_paths[p].dir_list;
  578.     }
  579.  
  580.   return NULL;
  581. #endif
  582. }
  583. #endif
  584.